package cn.aoyu.test.aop;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @ClassName: SpringAopTestDemo
 * @Auther: xww
 * @Date: 2019/6/24 0024 17:42
 * @Description: 通过配置的方式来实现aop
 * @see org.springframework.aop.config.AopNamespaceHandler
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-aop-config.xml")
public class SpringAopConfigTestDemo {

	/**
	 * 在调用ClassPathXmlApplicationContext-->refresh()-->finishBeanFactoryInitialization(beanFactory)-->...-->doGetBean(..)
	 * -->createBean(..)-->doCreateBean(..)-->initializeBean(..)-->applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)的方法中进行Bean代理
	 *
	 * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean 在这个方法中为bean生成了aop需要的代理
	 */
	private static ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop-config.xml");

	@Test
	public void testAop() {
		/**
		 * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean 抽象类中调用了initializeBean方法是为了判断是否需要给类加代理
		 *
		 * 这里是去调用AbstractBeanFactory抽象类中的doGetBean方法,来实现aop
		 * @see org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
		 */
		Dao dao = (Dao) context.getBean("dao");
		/**
		 * dao.select()调用原理:
		 * 此时dao是JdkDynamicAopProxy代理类或者是ObjenesisCglibAopProxy代理类
		 * 针对目标对象为接口类，则采取JDK代理；对于其他的类，则采取CGLIB代理
		 *
		 * 调用dao.select()的时候，会去调用JdkDynamicAopProxy中的invoke方法
		 * @see org.springframework.aop.framework.JdkDynamicAopProxy#invoke
		 *
		 * 调用JdkDynamicAopProxy类中的invoke() -->
		 * 调用JdkDynamicAopProxy类中的invocation.proceed() -->
		 * 调用ReflectiveMethodInvocation类中的proceed()方法 -->
		 * 调用ReflectiveMethodInvocation类中的return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this)方法
		 * 然后依次去调用以下两个方法:
		 * MethodBeforeAdviceInterceptor类中的invoke(..)方法和AspectJAfterAdvice类中的invoke(..)方法
		 */
		dao.select();
	}

}
